package graphics;

import javax.swing.JPanel;
import javax.swing.ButtonGroup; 
// import javax.swing.ImageIcon;

import java.awt.*;
import java.awt.image.*;
import javax.swing.*;

// import java.awt.image.BufferedImage;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

import java.util.Observer;
import java.util.Observable;
import java.util.List;
import java.util.LinkedList;

import model.City;
import model.util.Vector3D;

public class GCity extends JPanel implements Observer {

    /** Variables de classe */
    final public static int largeur = 400, hauteur = 300;
    
    /** Attributs */
    private City m_city;
    private Vector3D m_centerVector;
    private double m_scale;
    private GBuildingFactory m_gBuildingFactory;
    private ButtonGroup m_buttonGroup;
    private List<GBuilding> m_gBuildingList;
    private TexturePaint m_backgroundTexture;

    /** Constructeurs */
    public GCity(City city, ButtonGroup buttonGroup) {
        super(null);
        setPreferredSize(new Dimension(largeur, hauteur));
        m_city = city;
        m_buttonGroup = buttonGroup;
        m_centerVector = new Vector3D(0,0);
        m_scale = 1;
        BufferedImage image = graphics.util.GUtils.toBufferedImage(new ImageIcon("medias/sprites/Grass.png").getImage());
        m_backgroundTexture = new TexturePaint(image, new Rectangle(0, 0, image.getWidth(), image.getHeight())); 
        m_gBuildingList = new LinkedList<GBuilding>();
        m_gBuildingFactory = null;
        addMouseListener(new MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    if (hasGBuildingFactory() && getGBuildingFactory().canConstruct(e.getPoint())) construct(e.getPoint()); 
                }
            });
        addMouseWheelListener(new MouseWheelListener() {
                public void mouseWheelMoved(MouseWheelEvent e) {
                    double scale = getScale();
                    setScale(scale + e.getWheelRotation() * e.getScrollAmount() * 0.1);
                    Vector3D dist = calculVector3DFrom(e.getPoint()).sub(getCenterVector());
                    setCenterVector(getCenterVector().sub(dist.times(1-scale/getScale())));
                    refresh();
                }
            });
        addComponentListener(new ComponentAdapter() {
                public void componentResized(ComponentEvent e) { refresh(); }
            });
    }

    /** Accesseurs */
    public GBuildingFactory getGBuildingFactory() { return m_gBuildingFactory; }
    public Vector3D getCenterVector() { return m_centerVector; }
    public Point getCenterPoint() { return new Point(getWidth() / 2, getHeight() / 2); }
    public double getScale() { return m_scale; }
    public City getCity() { return m_city; }

    /** Mutateurs */
    public void setGBuildingFactory(GBuildingFactory gBuildingFactory) { m_gBuildingFactory = gBuildingFactory; }
    public void unsetGBuildingFactory() { setGBuildingFactory(null); }
    public void setCenterVector(Vector3D centerVector) { m_centerVector = centerVector; }
    public void setScale(double scale) { m_scale = Math.max(scale, 1); }

    /** Predicats */
    public boolean hasGBuildingFactory() { return getGBuildingFactory() != null; }
    
    /** Methodes */
    public Vector3D calculVector3DFrom(Point point) {
        return new Vector3D((point.getX() - getCenterPoint().getX()) * getScale() + getCenterVector().getX(),
                            (point.getY() - getCenterPoint().getY()) * getScale() + getCenterVector().getY());
    }

    public Point calculPointFrom(Vector3D vector3d) {
        Point centerPoint = getCenterPoint();
        return new Point((int)((vector3d.getX() - getCenterVector().getX()) / getScale() + centerPoint.getX()),
                         (int)((vector3d.getY() - getCenterVector().getY()) / getScale() + centerPoint.getY()));
                         
    }

    public Vector3D calculSizeVector3DFrom(Point size) 
    { return new Vector3D(size.getX() * getScale(), size.getY() * getScale()); }

    public Point calculSizePointFrom(Vector3D size) 
    { return new Point((int)(size.getX() / getScale()), (int)(size.getY() / getScale())); }

    public void update(Observable city, Object arg) 
    { throw new RuntimeException("ToDo"); }
    
    public void construct(Point point) {
        GBuilding gBuilding = getGBuildingFactory().construct(point);
        m_buttonGroup.add(gBuilding);
        m_gBuildingList.add(gBuilding);
        add(gBuilding);
    }

    public void refresh() {
        removeAll();
        for (GBuilding gBuilding : m_gBuildingList) add(gBuilding);
    }

    protected final void add(GBuilding gBuilding) {
        Vector3D vectorSize = gBuilding.getBuilding().getObject3D().getShape().getWrapSize();
        Vector3D vectorPosition = gBuilding.getBuilding().getObject3D().getPosition();
        Point size = calculSizePointFrom(vectorSize);
        Point position = calculPointFrom(vectorPosition.sub(vectorSize.divide(2)));
        super.add(gBuilding);
        gBuilding.setBounds((int)position.getX(),(int)position.getY(),(int)size.getX(),(int)size.getY());
    }

    public void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D)g; 
        g2d.setPaint(m_backgroundTexture);
        g2d.fillRect(0, 0, getWidth(), getHeight() );
        super.paintComponents(g);
    }
}